package org.yray;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.URI;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.FileSystem;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * 2022-06-01
 * 压缩指定目录下的全部文件
 *
 * @author tao.zhang
 * @since 1.0
 */
@Slf4j
public class ZipFileUtil {
    private ZipFileUtil() {
    }

    /**
     * 压缩指定目录下的全部文件
     *
     * @param directory 指定文件夹路径
     * @param zipFile   生成压缩包保存路径+文件名 (避免与指定文件夹在同一目录)
     * @throws IOException IOException
     */
    public static void compressToZip(String directory, File zipFile) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(zipFile);
             ZipOutputStream zipOut = new ZipOutputStream(fos);
             WritableByteChannel writableByteChannel = Channels.newChannel(zipOut)) {
            List<String> filePathList = new ArrayList<>(10);
            collectFilePath(directory, filePathList);
            for (String filePath : filePathList) {
                try (FileInputStream fis = new FileInputStream(filePath);
                     FileChannel fileChannel = fis.getChannel()) {
                    File file = new File(filePath);
                    ZipEntry zipEntry = new ZipEntry(file.getName());
                    zipOut.putNextEntry(zipEntry);
                    long size = fileChannel.size();
                    fileChannel.transferTo(0, size, writableByteChannel);
                }
            }
            zipOut.flush();
        }
    }

    /**
     * 获取指定文件夹下的全部文件路径
     *
     * @param directory    指定文件夹路径
     * @param filePathList 全部文件路径集合
     */
    private static void collectFilePath(String directory, List<String> filePathList) {
        File root = new File(directory);
        File[] files = root.listFiles();
        assert files != null;
        if (files.length == 0) {
            log.info("empty file directory");
            return;
        }
        Arrays.stream(files).forEach(file -> {
            try {
                if (file.isDirectory()) {
                    collectFilePath(file.getCanonicalPath(), filePathList);
                } else {
                    filePathList.add(file.getCanonicalPath());
                }
            } catch (IOException ex) {
                log.error("failed to get file path", ex);
            }
        });
    }

    public static void addFileToZip(String zipFilePath, String sourceFilePath) throws IOException {
        // 获取zip文件系统的路径
        Path zip = Paths.get(zipFilePath);
        URI uri = URI.create("jar:" + zip.toUri());
        // 获取zip文件系统
        FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap());
        // 源文件路径
        Path srcFile = Paths.get(sourceFilePath);
        // 在zip文件系统中找到要存放新文件的路径
        Path destPathInZip = fs.getPath("/" + srcFile.getFileName());
        // 将源文件复制到zip文件中
        Files.copy(srcFile, destPathInZip, StandardCopyOption.REPLACE_EXISTING);
        // 关闭zip文件系统
        fs.close();
    }

    public static void addFileToZip2(String zipFilePath, String sourceFilePath) throws IOException {
        // 创建一个新的临时文件，用于保存更新后的zip内容
        File tempFile = File.createTempFile("tempfile", ".zip");
        tempFile.deleteOnExit();

        try (FileInputStream fis = new FileInputStream(zipFilePath);
             FileOutputStream fos = new FileOutputStream(tempFile);
             ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
             ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos))) {

            // 复制原zip包的所有条目
            ZipEntry entry;
            // 添加新文件到zip
            File fileToAdd = new File(sourceFilePath);
            while ((entry = zis.getNextEntry()) != null) {
                if(!entry.getName().equals(fileToAdd.getName()))
                {
                    zos.putNextEntry(new ZipEntry(entry));
                    byte[] bytes = new byte[1024];
                    int length;
                    while ((length = zis.read(bytes)) >= 0) {
                        zos.write(bytes, 0, length);
                    }
                }
                zos.closeEntry();
            }


            ZipEntry newEntry = new ZipEntry(fileToAdd.getName());
            zos.putNextEntry(newEntry);

            try (FileInputStream fisToAdd = new FileInputStream(fileToAdd)) {
                byte[] buffer = new byte[1024];
                int read;
                while ((read = fisToAdd.read(buffer)) != -1) {
                    zos.write(buffer, 0, read);
                }
            }

            // 关闭新添加文件的条目
            zos.closeEntry();

            zis.close();
            // 完成后，替换原zip文件
            //Files.move(tempFile.toPath(), Paths.get(zipFilePath), StandardCopyOption.REPLACE_EXISTING);
        }
    }
    public static void main(String[] args) throws IOException, InterruptedException {
        /*String path = "D:\\用户\\Downloads\\RW20220620000276";
        File zipFile = new File("D:\\用户\\Downloads\\test\\RW20220620000276\\testzip.zip");
        compressToZip(path, zipFile);
        FileUtils.deleteDirectory(new File("D:\\用户\\Downloads\\test\\RW20220620000276"));*/
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                long begin=System.currentTimeMillis();
                for(int i=0;i<100;i++)
                {
                    try {
                        addFileToZip2("D:\\test\\jd_test2.zip","D:\\test\\jd_test.pdf");
                    }catch (Exception e) {
                        e.printStackTrace();
                     }

                }
                System.out.println("addFileToZip2:"+(System.currentTimeMillis()-begin));
            }
        });

        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                long begin=System.currentTimeMillis();
                for(int i=0;i<100;i++)
                {
                    try {
                        addFileToZip("D:\\test\\jd_test.zip","D:\\test\\jd_test.pdf");
                    }catch (Exception e) {
                        e.printStackTrace();
                    }

                }
                System.out.println("addFileToZip:"+(System.currentTimeMillis()-begin));
            }
        });

        t1.start();
        t2.start();
    }

}
